package gbench.sandbox.plot.data;

import static gbench.common.tree.LittleTree.kvp_int;
import static gbench.common.tree.LittleTree.IRecord.REC;

import java.io.File;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import gbench.common.fs.FileSystem;
import gbench.common.matlib.MatlibCanvas.Range;
import gbench.common.tree.LittleTree.IRecord;

/**
 * 文本数据读取&变换器
 * @author gbench
 *
 */
public class DataReader {
    
    /**
     * 从数据文件里面提取数据
     * @param datafile 源文件名称
     * @return {表名,rec结构的数据表},数据表 是一个带有名称的列向量集合,列结构为:(列名->[列数据元素])
     */
    public static IRecord load(final File datafile) {
        
        final var tables = REC();// 数据表集合
        final var dataTables = getDataTables(datafile);// 读成 {表名->[line]}的rec结构。
        final Predicate<List<String>> is_long_format = (ll)->ll.get(0).strip().startsWith("|"); // 是否长结构表，长表结构的首行为表头 以|开头。
        
        dataTables.foreach((tableName, data) -> {
            @SuppressWarnings("unchecked")
            final var tableLines = (List<String>)data;
            if(tableLines.size()<1) {
                System.err.println("表"+tableName+"没有数据！");
                return;
            }
            final var table = is_long_format.test(tableLines)?read_long_format(tableLines):read_width_format(tableLines);
            tables.add(tableName,table);
        });// foreach 数据遍历
        
        return tables;
    }
    
    /**
     * 从源文件读取所有的数据表，一个datafile可以保存有多张数据表。
     * @param datafile 数据文件 由一系列的 长/宽表的数据构成。<br>
     * 不同表之间采用 -- 表名 进行分隔 <br>
     * -- 表示注释行<br>
     * @return IRecord {表名,该表下的所有行TableLines的LIST}
     */
    public static IRecord getDataTables(final File datafile) {
        
        final var dataTables = REC(); // 数据表
        final var ar_table_name = new AtomicReference<String>();
        final var ar_table_body = new AtomicReference<LinkedList<String>>();
        final var pattern = Pattern.compile("^--\\s+(.*)$"); // 表头行
        final Consumer<String> data_table_builder = (name)->{//把 header 和 body 添加到  结果集合之中
            final var tblname = name==null?ar_table_name.get():ar_table_name.getAndSet(name); // 数据表名
            final var body = ar_table_body.getAndSet(new LinkedList<>());// 数据表体
            if (tblname != null && body != null)  dataTables.add(tblname,body);
        };// 数据表构建器
        
        FileSystem.utf8lines(datafile)// 读取数据文件
        .filter(line->!line.matches("(^\\s*$)"))
        .forEach(line->{
            final var matcher = pattern.matcher(line);
            if (matcher.matches()) {
                data_table_builder.accept(matcher.group(1));
                return;
            } else {
                ar_table_body.updateAndGet(u->{
                    (u==null? new LinkedList<String>(): u).add(line);
                    return u;
                });
            }// if
        });//forEach
        
        data_table_builder.accept(null);
        
        return dataTables;
    }

    /**
     * 从源文件里面读取数据
     * @param tableLines 数据表行：宽结构
     * @return 命名列向量结构的 KVP集合IRecord
     */
    public static IRecord read_width_format(final List<String> tableLines) {
        
        final var rec = REC();
        final var pattern = Pattern.compile("(([^\\|]+)\\|)?(.*)$");
        tableLines.stream().filter(line->!line.matches("(^\\s*$)|(^--\\s+.*$)"))
        .forEach(line->{
            final var matcher = pattern.matcher(line);
            if(matcher.matches()) {
                final var name = matcher.group(2).trim();
                final var data = matcher.group(3).trim();
                rec.add(name,data.split("\\s+"));
            }//if
        });//forEach
        
        return rec;
    }
    
    /**
     * 从源文件里面读取数据
     * @param tableLines 数据表行:长结构
     * @return 命名列向量结构的 KVP集合IRecord
     */
    public static IRecord read_long_format(final List<String> tableLines) {
        
        final var rec = REC();
        final var headerline = tableLines.get(0).strip().substring(1).strip();
        Arrays.stream(headerline.split("\\s+")).map(e->e.strip()).forEach(h -> rec.add(h, new LinkedList<String>()));
        final var size = rec.size();
        
        (tableLines.subList(1, tableLines.size())).stream() // 除掉第一行：表头
        .filter(line->!line.matches("(^\\s*$)|(^--\\s+.*$)"))
        .forEach(line -> {
           Arrays.stream(line.split("\\s+")).map(kvp_int()).forEach( tup -> {
               rec.get(tup._1()%size, (List<String>u)->{
                   u.add(tup._2());
                   return u;
               });
           });
        });//forEach
        
        return rec;
    }
    
    /**
     * 绘图变换器
     * @param rng 绘图空间 像素坐标
     * @param values 业务数据空间（业务数据值)，需要包含最大，最小值
     * @return 绘制变换器:数据空间数值x->绘图的像素坐标, 业务数据空间 的值 所对应的 绘图空间 中的像素坐标。
     */
    public static Function<Double,Double> transformer(final Range rng,final List<Double> values){
        
        final var stats = values.stream().collect(Collectors.summarizingDouble(e->e));//数据空间的统计
        final var max = stats.getMax();
        final var min = stats.getMin();
        final var len = max-min;// 数据空间的区间大小
        final var ratio = Math.abs((rng.end()-rng.start()))/len; // 单位数量空间所占据的绘图空间。
        
        return x->(x-min)*ratio+rng.start();
    }
    
    /**
     * 绘图变换器
     * @param rng 绘图空间 像素坐标
     * @param values 业务数据空间（业务数据值),需要包含最大，最小值
     * @return 绘制变换器:绘图空间坐标x->业务数据空间数值,绘图空间中的位置坐标,位于 业务数据空间中的值 的大小
     */
    public static Function<Double,Double> detransformer(final Range rng,final List<Double> values){
        
        final var stats = values.stream().collect(Collectors.summarizingDouble(e->e)); // 数据空间统计
        final var max = stats.getMax();
        final var min = stats.getMin();
        final var len = max-min;// 数据空间的区间大小
        final var ratio = Math.abs((rng.end()-rng.start()))/len; // 单位数量空间所占据的绘图空间。
        
        return c->(c-rng.start())/ratio+min;
    }

}
